﻿Imports System.Net.Sockets
Imports System.Net
Imports System.Text

Public Class FrmServer
    Private mTCPServer As Socket = Nothing
    Private mListen As Boolean
    Private mTestMode As Boolean '压力测试模式
    Private mClientList As Collection

    Private mReadByteCount As Long

    ''' <summary>
    ''' 软件字符编码集
    ''' </summary>
    ''' <remarks></remarks>
    Private EnCode As Encoding = Encoding.GetEncoding("gb2312")


    Private Sub ButListener_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButListener.Click

        If Not mListen Then
            Dim lListenerPort As Long
            '开始监听

            If Not Long.TryParse(TxtPort.Text, lListenerPort) Then
                MsgBox("请输入正确的监听端口号！", 64, "提示")
                Return
            End If

            Dim ipdtl As IPDetial = TryCast(cmbLocalNetAddr.SelectedItem, IPDetial)
            Dim localEndPoint As IPEndPoint
            If ipdtl Is Nothing Then
                localEndPoint = New IPEndPoint(IPAddress.Any, lListenerPort) '创建一个绑定点
            Else
                Dim ipAddr As IPAddress = ipdtl.Addr

                localEndPoint = New IPEndPoint(ipAddr, lListenerPort) '创建一个绑定点
            End If


            '            Try
            '                '获取本地监听信息
            '                Dim ipHostInfo As IPHostEntry '获取本机IP地址
            '                TxtDebug.Text += "本机名称：" + Dns.GetHostName() + vbNewLine
            '                ipHostInfo = Dns.GetHostEntry(Dns.GetHostName())
            '                For Each oIP As IPAddress In ipHostInfo.AddressList
            '                    Dim sIPInfo As String
            '                    Dim sType As String
            '                    If oIP.IsIPv6LinkLocal Then
            '                        sType = "IPV6 地址"
            '                    Else
            '                        sType = "IPV4 地址"
            '                    End If
            '                    sIPInfo = String.Format("IP：{0},协议类型：{1}{2}", oIP.ToString(), sType, vbNewLine)

            '                    TxtDebug.Text += sIPInfo
            '                Next

            '            Catch ex As Exception
            '                TxtDebug.Text += $"获取本机IP地址表时发生错误！
            '{ex.ToString }"
            '                'Return
            '            End Try




            'Dim localEndPoint As New IPEndPoint(IPAddress.Any, lListenerPort) '创建一个绑定点

            '创建监听器
            mTCPServer = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            Try
                '将监听器绑定到本地IP的指定端口上
                mTCPServer.Bind(localEndPoint)


                mTCPServer.Listen(10000) '开始监听

                '等待连接请求，关联回调函数
                mTCPServer.BeginAccept(New AsyncCallback(AddressOf AcceptCallback), mTCPServer)


                SetText("开始监听成功，监听端口:" & localEndPoint.Port)
            Catch ex As Exception
                mTCPServer.Dispose()
                mTCPServer = Nothing
                SetText("开始监听失败，监听端口:" & localEndPoint.Port & vbNewLine & ex.Message)
            End Try

        Else



            '停止监听
            Call CloseClient()

        End If

        SetListenState(Not mListen)
    End Sub


    ''' <summary>
    ''' 关闭监听和客户端连接
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub CloseClient()
        '关闭监听
        Try
            mTCPServer.Shutdown(SocketShutdown.Both)
        Catch ex As Exception
        End Try

        Dim localEndPoint As IPEndPoint = mTCPServer.LocalEndPoint
        mTCPServer.Close()
        SetText("停止监听：" & localEndPoint.Address.ToString & ":" & localEndPoint.Port)

        If CmbClient.Items.Count > 0 Then
            '关闭客户端连接
            Dim i As Integer
            Dim oItem As SocketInfo
            Dim oClient As Socket
            For i = CmbClient.Items.Count - 1 To 0 Step -1
                oItem = CType(CmbClient.Items(i), SocketInfo)
                If oItem IsNot Nothing Then
                    SetText("关闭客户端连接：" & oItem.toString)
                    oClient = oItem.Socket
                    Try
                        '往客户端发送服务器关闭消息
                        oClient.Send(EnCode.GetBytes("@@<Socket:Close>"))
                        '关闭连接
                        oClient.Shutdown(SocketShutdown.Both)
                    Catch ex As Exception

                    End Try
                    
                    oClient.Close()
                    oClient = Nothing

                    If mClientList IsNot Nothing Then
                        SyncLock (mClientList)
                            CmbClient.Items.RemoveAt(i)
                        End SyncLock
                    Else
                        CmbClient.Items.RemoveAt(i)
                    End If
                End If
            Next
        End If
    End Sub

    ''' <summary>
    ''' 设置监听状态
    ''' </summary>
    ''' <param name="bListen"></param>
    ''' <remarks></remarks>
    Private Sub SetListenState(ByVal bListen As Boolean)
        TxtPort.ReadOnly = bListen
        If bListen Then
            ButListener.Text = "停止监听"
        Else
            ButListener.Text = "开始监听"
        End If
        mListen = bListen
    End Sub


    ''' <summary>
    ''' TCP 客户端，连接请求处理函数
    ''' </summary>
    ''' <param name="ar"></param>
    ''' <remarks></remarks>
    Public Sub AcceptCallback(ByVal ar As IAsyncResult)
        '首先获取监听器对象。
        Dim oServer As Socket = CType(ar.AsyncState, Socket)
        '然后获取请求连接的客户端信息
        Dim oClient As Socket = Nothing
        Try
            oClient = oServer.EndAccept(ar)
        Catch ex As SocketException
            Dim oState As New SocketInfo
            oState.Socket = oClient
            Select Case ex.SocketErrorCode
                Case 10054 '.连接被远程端重置
                    ClientClose(oState)
                Case 10060 '远程连接超时
                    ClientClose(oState)
                Case Else
                    MsgBox("AcceptCallback Err :" & ex.SocketErrorCode)
            End Select

            Return
        Catch ex As ObjectDisposedException
            Return 'Socket 已关闭。 
        End Try

        If mAutoCloseClient Then
            oClient.Close()
            oClient.Dispose()
            oClient = Nothing
            '继续监听客户端请求
            oServer.BeginAccept(New AsyncCallback(AddressOf AcceptCallback), oServer)
            Return
        End If


        '开始监听客户端发送来的数据
        Dim state As New SocketInfo
        state.Socket = oClient
        ReDim state.buffer(SocketInfo.BufferSize)
        state.DataType = SocketInfo.eDataType.dRead


        If mTestMode Then
            '压力测试模式只需要计数就可以了

            SyncLock (mClientList)
                mClientList.Add(state, state.toString)
            End SyncLock


        Else
            '设置保活包
            Dim inPar(), outPar(), intbyte() As Byte
            ReDim inPar(11), outPar(50)
            'struct tcp_keepalive {
            '    ULONG onoff; //是否启用Keep-Alive
            '    ULONG keepalivetime; //多长时间（ms）没有数据就开始send心跳包
            '    ULONG keepaliveinterval;//两次KeepAlive探测间的时间间隔（单位：毫秒）
            '重复发送一定次数（2000 XP 2003 系统默认为5次, Vista 后的系统默认为10次）没有回应后超时
            '};
            '是否启用Keep-Alive
            inPar(0) = 1
            '多长时间后开始第一次探测（单位：毫秒）
            intbyte = ModTool.Int32ToByte(120000)
            Array.Reverse(intbyte)
            Array.Copy(intbyte, 0, inPar, 4, 4)
            '探测时间间隔（单位：毫秒）
            intbyte = ModTool.Int32ToByte(3000)
            Array.Reverse(intbyte)
            Array.Copy(intbyte, 0, inPar, 8, 4)

            Dim rtn As Integer = state.Socket.IOControl(IOControlCode.KeepAliveValues, inPar, outPar)

            '加入客户端信息至输出窗口
            DebugClient(state)
        End If

        '加入客户端至客户端列表
        AddClient(state)

        '开始监听客户端数据
        oClient.BeginReceive(state.buffer, 0, SocketInfo.BufferSize, 0, New AsyncCallback(AddressOf ReadCallback), state)

        '继续监听客户端请求
        oServer.BeginAccept(New AsyncCallback(AddressOf AcceptCallback), oServer)
    End Sub 'AcceptCallback    


    ''' <summary>
    ''' 添加客户端到列表
    ''' </summary>
    ''' <param name="oClient"></param>
    ''' <remarks></remarks>
    Delegate Sub AddClientCallback(ByRef oClient As SocketInfo)

    ''' <summary>
    ''' 添加客户端到列表
    ''' </summary>
    ''' <param name="oClient"></param>
    ''' <remarks></remarks>
    Private Sub AddClient(ByRef oClient As SocketInfo)
        If Me.CmbClient.InvokeRequired Then
            Dim callback As New AddClientCallback(AddressOf AddClient)
            Me.Invoke(callback, New Object() {oClient})
        Else
            If mTestMode Then
                SetClientCount(mClientList.Count)
            Else
                CmbClient.Items.Add(oClient)
                CmbClient.SelectedItem = oClient
            End If
            
        End If
    End Sub

    ''' <summary> 设置已连接到服务器的客户端数量 </summary>
    Delegate Sub SetClientCountCallback(ByVal lCount As Long)
    ''' <summary> 设置已连接到服务器的客户端数量 </summary>
    Private Sub SetClientCount(ByVal lCount As Long)
        If Me.TxtClientCount.InvokeRequired Then
            Dim callback As New SetClientCountCallback(AddressOf SetClientCount)
            Me.Invoke(callback, New Object() {lCount})
            Return
        End If

        TxtClientCount.Text = lCount
    End Sub


    ''' <summary> 从客户端列表中删除客户端信息 </summary>
    Delegate Sub DeleteClientCallback(ByRef oClient As SocketInfo)
    ''' <summary> 从客户端列表中删除客户端信息 </summary>
    Private Sub DeleteClient(ByRef oClient As SocketInfo)

        If Me.CmbClient.InvokeRequired Then
            Dim callback As New AddClientCallback(AddressOf DeleteClient)
            If oClient Is Nothing Then Return
            Me.Invoke(callback, New Object() {oClient})
        Else
            Dim sText As String, i As Integer
            Dim oItem As SocketInfo
            Try
                '从客户端列表删除
                sText = oClient.toString
                For i = 0 To Me.CmbClient.Items.Count - 1
                    oItem = CType(Me.CmbClient.Items(i), SocketInfo)
                    If oItem.toString = sText Then
                        If mClientList IsNot Nothing Then
                            SyncLock (mClientList)
                                CmbClient.Items.RemoveAt(i)
                            End SyncLock
                        Else
                            CmbClient.Items.RemoveAt(i)
                        End If

                    End If
                Next
            Catch ex As Exception
                'Debug.Print(ex.ToString)
            End Try
            

            '关闭连接
            oClient.Socket.Shutdown(SocketShutdown.Both)
            oClient.Socket.Close()
        End If
    End Sub



    ''' <summary>
    ''' 设置输出窗口文本的委托
    ''' </summary>
    ''' <param name="text"></param>
    ''' <remarks></remarks>
    Delegate Sub SetDebugTextCallback(ByVal [text] As String)



    ''' <summary>
    ''' 设置输出窗口文本
    ''' </summary>
    ''' <param name="text"></param>
    ''' <remarks></remarks>
    Private Sub SetText(ByVal [text] As String)
        If (Not chkShowLog.Checked) Then Return

        If Me.TxtDebug.InvokeRequired Then
            Dim d As New SetDebugTextCallback(AddressOf SetText)
            Me.Invoke(d, New Object() {[text]})
        Else
            Me.TxtDebug.Text = Format(Date.Now, "HH:mm:ss") & "--" & [text] & vbNewLine & TxtDebug.Text
            If TxtDebug.Text.Length > 20000 Then
                Me.TxtDebug.Text = Me.TxtDebug.Text.Substring(0, 13000)
            End If
        End If

    End Sub

    Delegate Sub SetReadCountCallback(ByVal [text] As String)
    Private Sub SetReadCount(ByVal sText As String)
        If Me.TxtReadByteCount.InvokeRequired Then
            Dim d As New SetReadCountCallback(AddressOf SetReadCount)
            Me.Invoke(d, New Object() {sText})
        Else
            Me.TxtReadByteCount.Text = sText
        End If
    End Sub



    ''' <summary>
    ''' 输出接入的客户端信息
    ''' </summary>
    ''' <param name="oClient"></param>
    ''' <remarks></remarks>
    Private Sub DebugClient(ByRef oClient As SocketInfo)
        Dim sText As String

        sText = "有客户端接入：IP：" & oClient.toString
        SetText(sText)
    End Sub

    ''' <summary>
    ''' 处理客户端发送数据的函数
    ''' </summary>
    ''' <param name="ar"></param>
    ''' <remarks></remarks>
    Public Sub ReadCallback(ByVal ar As IAsyncResult)
        Dim sRead As String = String.Empty '接收到的数据明文

        '获取客户端状态对象
        Dim state As SocketInfo = CType(ar.AsyncState, SocketInfo)
        '获取客户端 Socket
        Dim oClient As Socket = state.Socket

        '获取缓存中数据长度   
        Dim bytesRead As Integer
        Try
            bytesRead = oClient.EndReceive(ar)
        Catch ex As SocketException
            Select Case ex.SocketErrorCode
                Case 10054 '远程连接被关闭 ConnectionReset
                    ClientClose(state)

                    Return
                Case 10060 'TimedOut 
                    ClientClose(state)

                    Return
                Case Else
                    MsgBox("ReadCallback Err" & ex.SocketErrorCode)
            End Select
        Catch ex As ObjectDisposedException
            Return
        End Try



        If bytesRead > 0 Then
            mReadByteCount += bytesRead


            Dim bRead() As Byte = copyOfRange(state.buffer, 0, bytesRead)
            If ChkAutoSend.Checked Then
                oClient.Send(bRead)
                '继续读取缓存
                oClient.BeginReceive(state.buffer, 0, SocketInfo.BufferSize, 0, New AsyncCallback(AddressOf ReadCallback), state)
                Return
            End If

            SetReadCount(mReadByteCount.ToString)
            '转换数组为字符串
            If ChkHex.Checked = False Then
                sRead = EnCode.GetString(bRead)
            Else
                sRead = ModTool.ByteToHex(bRead)
            End If

            

            '检查是否客户端连接断开指令
            If sRead.IndexOf("@@<Socket:Close>") > -1 Then
                ClientClose(state)
                Return
            End If


            If Not mTestMode Then
                DebugClientRead(oClient, bytesRead, sRead)

                'CheckReadCMD(sRead, oClient)

                'ClientClose(state)
                'Return
            End If



            Try
                '继续读取缓存
                oClient.BeginReceive(state.buffer, 0, SocketInfo.BufferSize, 0, New AsyncCallback(AddressOf ReadCallback), state)
            Catch ex As ObjectDisposedException
                Return
            End Try
        Else
            ClientClose(state)
            Return
        End If

    End Sub 'ReadCallback    

    ''' <summary>
    ''' 输出客户返回的数据
    ''' </summary>
    ''' <param name="sText"></param>
    ''' <remarks></remarks>
    Private Sub DebugClientRead(ByRef oClient As Socket, ByVal lByteCount As Integer, ByVal sText As String)
        Dim oIP As IPEndPoint
        Dim sValue As String = "客户端：%1:%2,接收字节：%3；接收数据："

        Try
            oIP = oClient.RemoteEndPoint
            sValue = ReplaceEx(sValue, {oIP.Address.ToString, oIP.Port, lByteCount})
            sValue = sValue & sText

            SetText(sValue)
        Catch ex As Exception

        End Try
        
    End Sub

    ''' <summary>
    ''' 关闭客户端
    ''' </summary>
    ''' <param name="oClient"></param>
    ''' <remarks></remarks>
    Public Sub ClientClose(ByRef oClient As SocketInfo)
        If Not mTestMode Then
            SetText("客户端：" & oClient.toString & "，已断开连接！")
            '删除客户端
            DeleteClient(oClient)
        Else
            DeleteTestClient(oClient)
        End If
    End Sub

    ''' <summary>
    ''' 从客户端列表中删除指定的客户端
    ''' </summary>
    ''' <param name="oClient"></param>
    ''' <remarks></remarks>
    Private Sub DeleteTestClient(ByRef oClient As SocketInfo)
        Dim sKey As String = oClient.toString
        If oClient.Socket IsNot Nothing Then
            '关闭连接
            Try
                oClient.Socket.Shutdown(SocketShutdown.Both)
            Catch ex As Exception

            End Try

            oClient.Socket.Close()

        End If
        oClient = Nothing


        '从列表中删除
        If mClientList.Contains(sKey) Then
            SyncLock (mClientList)
                mClientList.Remove(sKey)
            End SyncLock

            SetClientCount(mClientList.Count)
        End If
    End Sub

    ''' <summary>
    ''' 往客户端发送数据
    ''' </summary>
    ''' <param name="oClient"></param>
    ''' <param name="data"></param>
    ''' <remarks></remarks>
    Private Sub Send(ByVal oClient As Socket, ByVal data As String)
        '转换明文为字节数组     
        Dim byteData As Byte() = {}
        If ChkHex.Checked = False Then
            byteData = EnCode.GetBytes(data)
        Else
            byteData = ModTool.HexToByte(data)
        End If



        '异步发送  

        Dim state As SocketInfo = New SocketInfo
        state.Socket = oClient
        state.buffer = byteData
        Call state.Stringbuffer.Append(data) '
        Try
            oClient.BeginSend(byteData, 0, byteData.Length, 0, New AsyncCallback(AddressOf SendCallback), state)
        Catch ex As SocketException
            Select Case ex.ErrorCode
                Case 10053 '连接被中止，由于超时或其他故障
                    ClientClose(state)
                    Return
                Case Else
                    MsgBox(ex.ErrorCode)
            End Select
        End Try

    End Sub 'Send    


    ''' <summary>
    ''' 发送数据完毕
    ''' </summary>
    ''' <param name="ar"></param>
    ''' <remarks></remarks>
    Private Sub SendCallback(ByVal ar As IAsyncResult)
        '获取客户端对象
        Dim state As SocketInfo = CType(ar.AsyncState, SocketInfo)
        Dim oClient As Socket = state.Socket

        '返回发送的字节数        
        Dim bytesSent As Integer = oClient.EndSend(ar)
        DebugClientSend(state, state.Stringbuffer.ToString, bytesSent)

    End Sub 'SendCallback


    ''' <summary>
    ''' 输出发送至客户端的内容
    ''' </summary>
    ''' <param name="oClient"></param>
    ''' <param name="sText"></param>
    ''' <param name="bytesSent"></param>
    ''' <remarks></remarks>
    Private Sub DebugClientSend(ByRef oClient As SocketInfo, ByVal sText As String, ByVal bytesSent As Integer)
        Dim sValue As String = "客户端：%1,发送字节：%2，内容：%3"
        sValue = ReplaceEx(sValue, {oClient.toString, bytesSent, sText})
        SetText(sValue)
    End Sub


    Private Sub FrmServer_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        '设置默认端口
        TxtPort.Text = 9687
        TxtTestPort.Text = 9688

        '获取本地网卡列表
        cmbLocalNetAddr.Items.Clear()
        Try
            '获取本地监听信息
            Dim ipHostInfo As IPHostEntry '获取本机IP地址
            TxtDebug.Text += "本机名称：" + Dns.GetHostName() + vbNewLine
            ipHostInfo = Dns.GetHostEntry(Dns.GetHostName())
            For Each oIP As IPAddress In ipHostInfo.AddressList
                Dim sIPInfo As String
                Dim sType As String
                If oIP.IsIPv6LinkLocal Then
                    sType = "IPV6 地址"
                Else
                    sType = "IPV4 地址"
                End If
                sIPInfo = String.Format("IP：{0},协议类型：{1}{2}", oIP.ToString(), sType, vbNewLine)
                cmbLocalNetAddr.Items.Add(New IPDetial(oIP))
                TxtDebug.Text += sIPInfo
            Next

        Catch ex As Exception
            TxtDebug.Text += $"获取本机IP地址表时发生错误！
{ex.ToString }"
            'Return
        End Try

        '设置初始状态
        SetListenState(False)
    End Sub


    Private Sub butClear_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles butClear.Click
        TxtDebug.Text = String.Empty
    End Sub

    Private Sub ButSend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButSend.Click
        If CmbClient.SelectedItem Is Nothing Then
            MsgBox("请选择一个接收数据的客户端！", 64, "提示")
            Return
        End If

        Dim oClient As SocketInfo = CType(CmbClient.SelectedItem, SocketInfo)
        Me.Send(oClient.Socket, TxtSend.Text)
        'TxtSend.Text = String.Empty
    End Sub

    Private Sub FrmServer_FormClosed(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles MyBase.FormClosed
        If mListen Then
            '停止监听
            Call CloseClient()
            SetListenState(Not mListen)
        End If

        If mTestMode Then
            Call CloseTestClient()
        End If
    End Sub

    ''' <summary>
    ''' 设置普通服务器操作界面是否可用。
    ''' </summary>
    ''' <param name="bValue"></param>
    ''' <remarks></remarks>
    Private Sub SetServerPanelEnabled(ByVal bValue As Boolean)
        ButListener.Enabled = bValue
        ButSend.Enabled = bValue
        CmbClient.Enabled = bValue
        TxtSend.ReadOnly = Not bValue
        TxtPort.ReadOnly = Not bValue
    End Sub


    Private Sub butTestSocket_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles butTestSocket.Click
        If mListen Then
            MsgBox("请首先停止服务器端口监听！", 64, "提示")
            Return
        End If

        If Not mTestMode Then
            Dim lListenerPort As Long
            '开始监听

            If Not Long.TryParse(TxtTestPort.Text, lListenerPort) Then
                MsgBox("请输入正确的监听端口号！", 64, "提示")
                Return
            End If




            '获取本地监听信息
            Dim ipHostInfo As IPHostEntry = Dns.GetHostEntry(Dns.GetHostName()) '获取本机IP地址
            Dim ipAddress As IPAddress = ipHostInfo.AddressList(0) '摘取本机IP地址
            Dim localEndPoint As New IPEndPoint(ipAddress, lListenerPort) '创建一个绑定点

            '创建监听器
            mTCPServer = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

            '创建客户端集合
            mClientList = New Collection

            SetClientCount(0)

            mReadByteCount = 0
            SetReadCount(mReadByteCount.ToString)

            '将监听器绑定到本地IP的指定端口上
            mTCPServer.Bind(localEndPoint)

            mTCPServer.Listen(5 * 10000) '开始监听 最大接受5万个连接请求

            '等待连接请求，关联回调函数
            mTCPServer.BeginAccept(New AsyncCallback(AddressOf AcceptCallback), mTCPServer)


            SetText("开始监听：" & localEndPoint.Address.ToString & ":" & localEndPoint.Port)
        Else
            '停止监听
            Call CloseTestClient()

        End If

        SetTestState(Not mTestMode)
    End Sub

    ''' <summary>
    ''' 设置测试面板状态
    ''' </summary>
    ''' <param name="bValue"></param>
    ''' <remarks></remarks>
    Private Sub SetTestState(ByVal bValue As Boolean)
        SetServerPanelEnabled(Not bValue)
        mTestMode = bValue
        TxtTestPort.ReadOnly = bValue
        If bValue Then
            butTestSocket.Text = "停止压力测试"
            TmrSend.Interval = 20000 '每隔5秒发送一次数据
        Else
            butTestSocket.Text = "开始压力测试"
        End If

        TmrSend.Enabled = bValue

    End Sub


    ''' <summary>
    ''' 关闭压力测试
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub CloseTestClient()
        '关闭监听
        Try
            mTCPServer.Shutdown(SocketShutdown.Both)
        Catch ex As Exception
        End Try

        Dim localEndPoint As IPEndPoint = mTCPServer.LocalEndPoint
        mTCPServer.Close()
        SetText("停止监听：" & localEndPoint.Address.ToString & ":" & localEndPoint.Port)

        If mClientList.Count > 0 Then
            '关闭客户端连接
            Dim i As Integer
            Dim oItem As SocketInfo
            Dim oClient As Socket
            For i = mClientList.Count To 1 Step -1
                oItem = CType(mClientList.Item(i), SocketInfo)
                If oItem IsNot Nothing Then
                    oClient = oItem.Socket

                    '往客户端发送服务器关闭消息
                    oClient.Send(EnCode.GetBytes("@@<Socket:Close>"))
                    '关闭连接
                    oClient.Shutdown(SocketShutdown.Both)
                    oClient.Close()
                    oClient = Nothing
                End If
            Next
        End If
        SetClientCount(0)

        '清空客户端列表
        mClientList.Clear()

        mClientList = Nothing
    End Sub


    Private Sub SendTest(ByVal oClient As SocketInfo)
        '转换明文为字节数组     
        Dim byteData As Byte() = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
                                  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
                                  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
                                  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

        '异步发送  

        Try
            oClient.Socket.BeginSend(byteData, 0, byteData.Length, 0, Nothing, Nothing)
        Catch ex As SocketException
            Select Case ex.ErrorCode
                Case 10053 '连接被中止，由于超时或其他故障
                    ClientClose(oClient)
                    Return
                Case Else
                    MsgBox(ex.ErrorCode)
            End Select
        Catch ex As ObjectDisposedException
            Return
        End Try
    End Sub 'Send  



    Private Sub TmrSend_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TmrSend.Tick
        '循环发送数据
        Dim i As Integer
        Dim oState As SocketInfo

        If mClientList.Count = 0 Then Return

        SyncLock mClientList
            For i = mClientList.Count To 1 Step -1
                oState = CType(mClientList.Item(i), SocketInfo)
                SendTest(oState)
            Next
        End SyncLock

    End Sub

    ''' <summary>
    ''' 检查接收到的指令是否需要进行回应
    ''' </summary>
    ''' <param name="sRead"></param>
    ''' <param name="oClient"></param>
    ''' <remarks></remarks>
    Private Sub CheckReadCMD(ByVal sRead As String, ByVal oClient As Socket)
        Dim sCMDList(13, 1) As String
        'LED液晶读卡器
        '读取地址
        sCMDList(0, 0) = "7E0100000100027E" '接收
        sCMDList(0, 1) = "7E02000101020001077E" '回应 地址1
        '写入地址
        sCMDList(1, 0) = "7E01000002020001067E" '接收
        sCMDList(1, 1) = "7E02000102020001087E" '回应 
        '读取标题
        sCMDList(2, 0) = "7E0100000300047E" '接收
        sCMDList(2, 1) = "7E020001032041C7F833C5C535B9F10000000000000000000000000000000000000000000000C27E" '回应 
        '写入标题
        sCMDList(3, 0) = "7E010000042041C7F833C5C535B9F10000000000000000000000000000000000000000000000C17E" '接收
        sCMDList(3, 1) = "7E0200010400077E" '回应 
        '警告通知
        sCMDList(4, 0) = "7E01000011020101167E" '接收
        sCMDList(4, 1) = "7E0200011100147E" '回应 

        'ID读卡器
        '读取地址
        sCMDList(5, 0) = "7E0100012100237E" '接收
        sCMDList(5, 1) = "7E02000121020001277E" '回应 地址1
        '写入地址
        sCMDList(6, 0) = "7E01000122020001277E" '接收
        sCMDList(6, 1) = "7E02000122020001287E" '回应 
        '读取标题
        sCMDList(7, 0) = "7E0100012300257E" '接收
        sCMDList(7, 1) = "7E02000123060089635200006A7E" '回应 
        '写入标题
        sCMDList(8, 0) = "7E01000124020000287E" '接收
        sCMDList(8, 1) = "7E0200012400277E" '回应 

        '控制板
        '读取物品检测功能
        sCMDList(9, 0) = "7E4D432D35383136413333303230303033FFFFFFFF1465C17941810100000000CF7E" '接收
        sCMDList(9, 1) = "7E1465C1794D432D35383136413333303230303033FFFFFFFF5181010000000100E07E" '回应 地址1
        '写入物品检测功能
        sCMDList(10, 0) = "7E4D432D35383136413333303230303033FFFFFFFF1465C1794181000000000100CF7E" '接收
        sCMDList(10, 1) = "7E1465C1794D432D35383136413333303230303033FFFFFFFF210100000000002E7E" '回应 
        '读取柜锁单元信息
        sCMDList(11, 0) = "7E4D432D35383136413333303230303033FFFFFFFF1465C1794182010000000101D27E" '接收
        sCMDList(11, 1) = "7E1465C1794D432D35383136413333303230303033FFFFFFFF5182010000000B010100AABBCC00BBCCAA01507E" '回应 
        '写入柜锁单元信息
        sCMDList(12, 0) = "7E4D432D35383136413333303230303033FFFFFFFF1465C17941820000000006010100AABBCC087E" '接收
        sCMDList(12, 1) = "7E1465C1794D432D35383136413333303230303033FFFFFFFF210100000000002E7E" '回应 
        '读取全部柜锁信息
        sCMDList(13, 0) = "7E4D432D35383136413333303230303033FFFFFFFF1465C17941820200000000D17E" '接收
        sCMDList(13, 1) = "7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C01010000000000000000010201000000000000000001030100000000000000000104010000000000000000011F7E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C05010000000000000000010601000000000000000001070100000000000000000108010000000000000000012F7E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C09010000000000000000010A010000000000000000010B010000000000000000010C010000000000000000013F7E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C0D010000000000000000010E010000000000000000010F0100000000000000000110010000000000000000014F7E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C11010000000000000000011201000000000000000001130100000000000000000114010000000000000000015F7E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C15010000000000000000011601000000000000000001170100000000000000000118010000000000000000016F7E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C19010000000000000000011A010000000000000000011B010000000000000000011C010000000000000000017F027E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C1D010000000000000000011E010000000000000000011F0100000000000000000120010000000000000000018F7E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C21010000000000000000012201000000000000000001230100000000000000000124010000000000000000019F7E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C2501000000000000000001260100000000000000000127010000000000000000012801000000000000000001AF7E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C29010000000000000000012A010000000000000000012B010000000000000000012C01000000000000000001BF7E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C2D010000000000000000012E010000000000000000012F010000000000000000013001000000000000000001CF7E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C3101000000000000000001320100000000000000000133010000000000000000013401000000000000000001DF7E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C3501000000000000000001360100000000000000000137010000000000000000013801000000000000000001EF7E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C39010000000000000000013A010000000000000000013B010000000000000000013C01000000000000000001FF7E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182020000002C3D010000000000000000013E010000000000000000013F0100000000000000000140010000000000000000010F7E" & _
"7E1465C1794D432D35383136413333303230303033FFFFFFFF5182F200000000D17E" '回应 

        For i As Integer = 0 To 13
            If sCMDList(i, 0) = sRead Then
                Send(oClient, sCMDList(i, 1))
                Exit For
            End If
        Next
    End Sub

    Private Sub BurReadEvent_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BurReadEvent.Click
        SendDataToClient("7E020001100400A105B16E7E") '读卡事件
    End Sub

    

    Private Sub ButTimeEvent_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButTimeEvent.Click
        SendDataToClient("7E0200011200157E") '校时事件
    End Sub

    Private Sub SendDataToClient(ByVal sSend As String)
        If CmbClient.SelectedItem Is Nothing Then
            MsgBox("请选择一个接收数据的客户端！", 64, "提示")
            Return
        End If

        Dim oClient As SocketInfo = CType(CmbClient.SelectedItem, SocketInfo)
        Me.Send(oClient.Socket, sSend)
        TxtSend.Text = String.Empty
    End Sub


    Private mAutoCloseClient As Boolean
    Private Sub chkAutoCloseClient_CheckedChanged(sender As Object, e As EventArgs) Handles chkAutoCloseClient.CheckedChanged
        mAutoCloseClient = chkAutoCloseClient.Checked
    End Sub
End Class




